from tkinter import messagebox, ttk
import os
from calc_utils import (convert_to_pixels, convert_from_pixels,
                        calculate_optimal_dpi_for_image)

class SizeMixin:

    def update_size_state(self):
        """更新尺寸相关控件的状态"""
        state = "normal" if self.size_enabled.get() else "disabled"

        # 更新所有相关控件的状态
        self.width_entry.configure(state=state)
        self.height_entry.configure(state=state)
        self.restore_size_btn.configure(state=state)

        # 检查图片是否都是相同尺寸
        is_uniform_size = False
        if self.all_images_info:
            is_uniform_size = all(img['width'] == self.all_images_info[0]['width'] and
                                  img['height'] == self.all_images_info[0]['height']
                                  for img in self.all_images_info)

        # 根据图片尺寸情况更新复选框状态
        if state == "normal":
            # 检查是否处于标准尺寸模式
            if self.is_standard_size_mode():
                # 标准尺寸模式：禁用所有保持比例功能
                self.ratio_checkbox.configure(state="disabled")
                self.batch_ratio_checkbox.configure(state="disabled")
                self.maintain_ratio.set(False)
                self.batch_maintain_ratio.set(False)
                
                # 标准尺寸模式下处理宽高输入框状态
                orientation_uniformity = self.check_orientation_uniformity()
                if len(self.selected_files) > 1 and (not is_uniform_size or not orientation_uniformity):
                    # 批量图片尺寸不统一或方向混合：禁用宽高输入框
                    self.width_entry.configure(state="disabled")
                    self.height_entry.configure(state="disabled")
                else:
                    # 单张图片或批量图片尺寸统一且方向一致：启用宽高输入框
                    self.width_entry.configure(state="normal")
                    self.height_entry.configure(state="normal")
            else:
                # 常规模式
                # 单张图片或批量图片尺寸均相同时
                if len(self.selected_files) == 1 or is_uniform_size:
                    # 启用"保持原图比例"，禁用"批量保持比例"
                    self.ratio_checkbox.configure(state="normal")
                    self.batch_ratio_checkbox.configure(state="disabled")
                    # 默认选中"保持原图比例"
                    self.maintain_ratio.set(True)
                    self.batch_maintain_ratio.set(False)
                # 批量图片尺寸不同时
                elif self.all_images_info and len(self.selected_files) > 1:
                    # 禁用"保持原图比例"，启用"批量保持比例"
                    self.ratio_checkbox.configure(state="disabled")
                    self.batch_ratio_checkbox.configure(state="normal")
                    # 默认取消选中"保持原图比例"
                    self.maintain_ratio.set(False)
                    # 不自动选中"批量保持比例"，由用户决定
                else:
                    # 其他情况，两个都禁用
                    self.ratio_checkbox.configure(state="disabled")
                    self.batch_ratio_checkbox.configure(state="disabled")
                    self.maintain_ratio.set(False)
                    self.batch_maintain_ratio.set(False)
        else:
            # 当尺寸修改禁用时，两个复选框都禁用
            self.ratio_checkbox.configure(state="disabled")
            self.batch_ratio_checkbox.configure(state="disabled")
            self.maintain_ratio.set(False)
            self.batch_maintain_ratio.set(False)

        # 更新单位选择按钮的状态
        for radio_button in self.unit_subframe.winfo_children()[1:]:  # 跳过Label
            if isinstance(radio_button, ttk.Radiobutton):
                radio_button.configure(state=state)

                # 如果重新采样关闭，禁用像素单位选择
                if not self.resampling_enabled.get() and radio_button.cget("value") == "pixel":
                    radio_button.configure(state="disabled")

                    # 如果当前选择的是像素单位，切换到厘米单位
                    if self.unit_var.get() == "pixel":
                        self.unit_var.set("cm")
                        self.update_size_display()  # 更新显示
                
                # 如果是标准尺寸模式，禁用像素单位选择
                if self.is_standard_size_mode() and radio_button.cget("value") == "pixel":
                    radio_button.configure(state="disabled")
                    
                    # 如果当前选择的是像素单位，切换到英寸单位
                    if self.unit_var.get() == "pixel":
                        self.unit_var.set("inch")
                        self.update_size_display()  # 更新显示

        # 如果取消勾选，重置修改标志并自动还原为原始尺寸
        if not self.size_enabled.get():
            self.size_modified = False
            self.restore_original_size()
        else:
            # 如果勾选了修改尺寸，确保尺寸输入框显示当前值
            # 这确保了当用户开启尺寸修改时能够看到当前图片的实际尺寸
            if hasattr(self, 'original_width') and hasattr(self, 'original_height'):
                # 检查图片尺寸是否统一
                _, uniform_width, uniform_height, _, uniform_size = self.check_images_uniformity()

                # 只有在图片尺寸统一或只有一张图片时才显示具体数值
                if uniform_size or len(self.selected_files) == 1:
                    current_dpi = float(self.dpi_var.get() if self.dpi_enabled.get() else self.original_dpi)
                    unit = self.unit_var.get()
                    if unit == "pixel":
                        self.width_var.set(str(self.original_width))
                        self.height_var.set(str(self.original_height))
                    else:
                        # 转换为当前单位
                        if unit == "cm":
                            width_cm = (self.original_width / current_dpi) * 2.54
                            height_cm = (self.original_height / current_dpi) * 2.54
                            self.width_var.set(f"{width_cm:.2f}")
                            self.height_var.set(f"{height_cm:.2f}")
                        elif unit == "inch":
                            width_inch = self.original_width / current_dpi
                            height_inch = self.original_height / current_dpi
                            self.width_var.set(f"{width_inch:.2f}")
                            self.height_var.set(f"{height_inch:.2f}")
                else:
                    # 图片尺寸不统一，显示空白
                    self.width_var.set("")
                    self.height_var.set("")
                    # 如果是标准尺寸模式，禁用输入框
                    if self.is_standard_size_mode():
                        self.width_entry.configure(state="disabled")
                        self.height_entry.configure(state="disabled")

        # 在最后更新批量保持比例模式下的输入框状态
        self.update_batch_ratio_state()

        # 检查重新采样状态
        # 如果重新采样关闭且既不保持原图比例也不批量保持比例，则自动开启重新采样
        if not self.resampling_enabled.get() and not (self.maintain_ratio.get() or self.batch_maintain_ratio.get()):
            self.resampling_enabled.set(True)
            messagebox.showinfo("提示",
                                "已自动开启重新采样模式。\n不重新采样模式仅在勾选'保持原图比例'或'批量保持比例'时有效。")
            self.update_resampling_state()

        # 检查是否进入或退出特殊模式，并应用规则 (在update_batch_ratio_state之后)
        is_special_mode = (self.batch_maintain_ratio.get() and
                           not self.resampling_enabled.get() and
                           self.unit_var.get() in ["cm", "inch"] and
                           self.size_enabled.get())
        if is_special_mode:
            self._apply_special_mode_rules()
        # 如果不是特殊模式，update_batch_ratio_state 已经处理了常规批量或禁用状态

    def restore_original_size(self):
        """还原为原始尺寸的方法"""
        # 首先检查图片尺寸是否统一
        _, uniform_width, uniform_height, _, uniform_size = self.check_images_uniformity()

        # 如果图片尺寸不统一，清空输入框
        if not uniform_size and len(self.selected_files) > 1:
            self.width_var.set("")
            self.height_var.set("")
            self.size_modified = False
            # 重置last_used_dpi为原始DPI
            if hasattr(self, 'original_dpi') and self.original_dpi is not None:
                self.last_used_dpi = float(self.original_dpi)
            self.update_size_preview()

            # 根据模式决定输入框状态
            if self.is_standard_size_mode():
                # 标准尺寸模式：禁用输入框
                self.width_entry.configure(state="disabled")
                self.height_entry.configure(state="disabled")
            elif self.batch_maintain_ratio.get() and self.size_enabled.get():
                # 批量保持比例模式：启用输入框
                self.width_entry.configure(state="normal")
                self.height_entry.configure(state="normal")

            return

        # 如果图片尺寸统一或只有一张图片，恢复原始尺寸
        if hasattr(self, 'original_width') and hasattr(self, 'original_height'):
            current_dpi = float(self.dpi_var.get() if self.dpi_enabled.get() else self.original_dpi)
            # 重置last_used_dpi为当前DPI
            self.last_used_dpi = current_dpi

            unit = self.unit_var.get()
            if unit == "pixel":
                self.width_var.set(str(self.original_width))
                self.height_var.set(str(self.original_height))
            else:
                # 转换为当前单位
                if unit == "cm":
                    width_cm = (self.original_width / current_dpi) * 2.54
                    height_cm = (self.original_height / current_dpi) * 2.54
                    self.width_var.set(f"{width_cm:.2f}")
                    self.height_var.set(f"{height_cm:.2f}")
                elif unit == "inch":
                    width_inch = self.original_width / current_dpi
                    height_inch = self.original_height / current_dpi
                    self.width_var.set(f"{width_inch:.2f}")
                    self.height_var.set(f"{height_inch:.2f}")

            # 重置修改标志
            self.size_modified = False
            # 更新预览
            self.update_size_preview()

            # 如果批量保持比例被勾选，根据图片的统一性决定输入框状态
            if self.batch_maintain_ratio.get() and self.size_enabled.get():
                if not uniform_size and len(self.selected_files) > 1:
                    # 如果是不同尺寸的批量图片，启用两个输入框
                    self.width_entry.configure(state="normal")
                    self.height_entry.configure(state="normal")
                    # 清空输入框
                    self.width_var.set("")
                    self.height_var.set("")

    def calculate_proportional_size(self, *args):
        """根据一个维度调整另一个维度，保持比例不变"""
        # 如果不需要保持比例，或者尺寸修改被禁用，则不处理
        if (not self.maintain_ratio.get() and not self.batch_maintain_ratio.get()) or not self.size_enabled.get():
            return

        # 防止递归调用
        if hasattr(self, '_updating_size'):
            return

        # 防止在调整DPI时触发（因为DPI调整会处理两个维度）
        if hasattr(self, '_updating_dpi'):
            return

        try:
            # 判断调用此函数的变量
            if not args or not args[0]:
                return

            # 获取当前单位和DPI
            current_unit = self.unit_var.get()
            dpi_var_value = self.dpi_var.get().strip()
            current_dpi = float(dpi_var_value) if dpi_var_value else float(self.original_dpi)

            # 设置标记防止递归
            self._updating_size = True

            # 添加一个标志表示正在进行比例计算
            self._in_proportion_calculation = True

            try:
                # 确定是宽度还是高度被修改了
                modified_field = "width" if args[0] == str(self.width_var) else "height"

                # 获取当前值
                width_str = self.width_var.get().strip()
                height_str = self.height_var.get().strip()

                # 批量图片处理时，批量保持比例模式下不执行原有的比例调整逻辑
                if len(self.selected_files) > 1 and not self.maintain_ratio.get():
                    return

                if self.batch_maintain_ratio.get():
                    # 批量保持比例模式下不需要执行计算
                    return

                # 执行原有的比例调整逻辑
                if modified_field == "width" and width_str:
                    try:
                        # 获取原始宽高比
                        aspect_ratio = float(self.original_height) / float(self.original_width)

                        # 将修改后的宽度转换为像素
                        modified_width_px = self.convert_to_pixels(width_str, current_unit, current_dpi)

                        # 计算对应的高度（以像素为单位）
                        corresponding_height_px = modified_width_px * aspect_ratio

                        # 转换回当前单位
                        if current_unit == "pixel":
                            # 像素：直接设置为整数
                            new_height = int(round(corresponding_height_px))
                        else:
                            # 厘米或英寸：转换为物理尺寸
                            new_height = self.convert_from_pixels(corresponding_height_px, current_unit, current_dpi)

                        # 只有当它不是用户输入的高度值时才更新
                        if not self.user_input_height:
                            # 格式化后设置高度
                            if current_unit == "pixel":
                                self.height_var.set(str(new_height))
                            else:
                                self.height_var.set(f"{new_height:.2f}")

                    except (ValueError, ZeroDivisionError):
                        return

                elif modified_field == "height" and height_str:
                    try:
                        # 获取原始宽高比的倒数
                        inv_aspect_ratio = float(self.original_width) / float(self.original_height)

                        # 将修改后的高度转换为像素
                        modified_height_px = self.convert_to_pixels(height_str, current_unit, current_dpi)

                        # 计算对应的宽度（以像素为单位）
                        corresponding_width_px = modified_height_px * inv_aspect_ratio

                        # 转换回当前单位
                        if current_unit == "pixel":
                            # 像素：直接设置为整数
                            new_width = int(round(corresponding_width_px))
                        else:
                            # 厘米或英寸：转换为物理尺寸
                            new_width = self.convert_from_pixels(corresponding_width_px, current_unit, current_dpi)

                        # 只有当它不是用户输入的宽度值时才更新
                        if not self.user_input_width:
                            # 格式化后设置宽度
                            if current_unit == "pixel":
                                self.width_var.set(str(new_width))
                            else:
                                self.width_var.set(f"{new_width:.2f}")

                    except (ValueError, ZeroDivisionError):
                        return

                # 确认是否修改了尺寸
                width_modified = width_str != str(self.original_width)
                height_modified = height_str != str(self.original_height)

                # 设置修改标志
                self.size_modified = width_modified or height_modified

            finally:
                # 移除递归保护标志
                delattr(self, '_updating_size')
                # 延迟一点时间再移除比例计算标志，确保计算完全结束
                self.root.after(100, lambda: delattr(self, '_in_proportion_calculation') if hasattr(self,
                                                                                                    '_in_proportion_calculation') else None)

        except Exception as e:
            print(f"比例计算出错: {e}")
            # 处理其他可能的异常


    def update_resampling_state(self):
        """更新重新采样相关的控件状态和计算方式"""
        # 获取当前单位
        current_unit = self.unit_var.get()

        # 获取所有单位单选按钮
        unit_buttons = [child for child in self.unit_subframe.winfo_children()[1:]
                        if isinstance(child, ttk.Radiobutton)]

        # 找到像素单位的单选按钮
        pixel_radio = None
        for button in unit_buttons:
            if button.cget("value") == "pixel":
                pixel_radio = button
                break

        # 重新采样关闭时的逻辑
        if not self.resampling_enabled.get():
            # 禁用像素单位选择，如果当前是像素单位则切换到厘米
            if pixel_radio:
                pixel_radio.configure(state="disabled")
                if current_unit == "pixel":
                    # 切换到厘米单位
                    self.unit_var.set("cm")
                    self.update_size_display()  # 更新显示

            # 检查标准尺寸为自定义时的特殊处理
            if (hasattr(self, 'standard_size_var') and 
                self.standard_size_var.get() == "自定义" and 
                not self.dpi_enabled.get()):
                # 标准尺寸为自定义且DPI关闭时，自动开启DPI
                self.dpi_enabled.set(True)
                self.update_dpi_state()

            # 检查是否满足不重新采样的有效条件
            if not (self.maintain_ratio.get() or self.batch_maintain_ratio.get()):
                # 如果既不保持原图比例也不批量保持比例，则自动勾选重新采样
                self.resampling_enabled.set(True)
                messagebox.showinfo("提示", "不重新采样模式仅在勾选'保持原图比例'或'批量保持比例'时有效。")
                # 再次调用以更新状态
                self.update_resampling_state()
                return

            # 批量保持比例模式下的特殊处理
            if self.batch_maintain_ratio.get():
                # 重置输入标志，确保在不重新采样模式下，DPI和尺寸输入互不影响
                self.user_input_width = False
                self.user_input_height = False
                self.user_input_dpi = False
                if hasattr(self, 'last_modified_field'):
                    delattr(self, 'last_modified_field')
                if hasattr(self, 'last_user_input_value'):
                    delattr(self, 'last_user_input_value')

                # 检查是否进入特殊模式并应用规则
                is_special_mode_after_resample_toggle = (self.batch_maintain_ratio.get() and
                                                         not self.resampling_enabled.get() and
                                                         self.unit_var.get() in ["cm", "inch"] and
                                                         self.size_enabled.get())
                if is_special_mode_after_resample_toggle:
                    self._apply_special_mode_rules()
                elif self.size_enabled.get():  # 如果不是特殊模式但尺寸修改启用，确保常规批量模式状态正确
                    self.update_batch_ratio_state()

        else:
            # 重新采样开启时，如果尺寸修改已启用，则启用像素单位选择
            if self.size_enabled.get() and pixel_radio:
                pixel_radio.configure(state="normal")

        # 更新计算和显示，考虑到不同场景（单张/批量，尺寸相同/不同，DPI相同/不同）
        if self.batch_maintain_ratio.get():
            # 批量保持比例模式下，不通过recalculate_all_values更新
            # 这里不做额外处理，因为批量保持比例模式下的计算是通过process_batch_ratio独立处理
            pass
        else:
            # 保持原图比例或普通模式下，通过recalculate_all_values更新
            self.recalculate_all_values()

        # 检查在切换重新采样后，是否进入或退出了特殊模式
        is_special_mode = (self.batch_maintain_ratio.get() and
                           not self.resampling_enabled.get() and
                           self.unit_var.get() in ["cm", "inch"] and
                           self.size_enabled.get())
        if is_special_mode:
            self._apply_special_mode_rules()
        elif self.batch_maintain_ratio.get() and self.size_enabled.get():  # 不是特殊模式，但可能是常规批量模式
            self.update_batch_ratio_state()
        # 如果也不是常规批量模式，则输入框状态由 update_size_state 等更通用的函数管理

    def _on_dpi_var_change_special_mode(self, *args):
        """当DPI变量在特殊模式下改变时调用"""
        is_special_mode = (self.batch_maintain_ratio.get() and
                           not self.resampling_enabled.get() and
                           self.unit_var.get() in ["cm", "inch"] and
                           self.size_enabled.get())
        if is_special_mode:
            self._apply_special_mode_rules(source_changed="dpi")
        # 如果不是特殊模式，则此trace不执行额外操作，
        # 其他已绑定的DPI trace (如 adjust_size_for_dpi_change) 会正常工作。

    def _apply_special_mode_rules(self, source_changed=None):
        """
        应用特殊模式下的输入框启用/禁用规则。
        特殊模式：批量保持比例 + 不重新采样 + 厘米/英寸单位 + 修改尺寸已启用。
        source_changed: "width", "height", "dpi"，或 None (表示由复选框/单选按钮改变触发)
        """
        width_str = self.width_var.get().strip()
        height_str = self.height_var.get().strip()
        dpi_str = self.dpi_var.get().strip()

        if source_changed == "width":
            if width_str:  # 用户正在输入或已输入宽度
                self.height_var.set("")
                self.height_entry.config(state="disabled")
                self.dpi_var.set("")
                self.dpi_entry.config(state="disabled")
                self.width_entry.config(state="normal")
            else:  # 用户清空了宽度输入框
                self._enable_all_inputs_for_special_mode()
        elif source_changed == "height":
            if height_str:  # 用户正在输入或已输入高度
                self.width_var.set("")
                self.width_entry.config(state="disabled")
                self.dpi_var.set("")
                self.dpi_entry.config(state="disabled")
                self.height_entry.config(state="normal")
            else:  # 用户清空了高度输入框
                self._enable_all_inputs_for_special_mode()
        elif source_changed == "dpi":
            if dpi_str:  # 用户正在输入或已输入DPI
                self.width_var.set("")
                self.width_entry.config(state="disabled")
                self.height_var.set("")
                self.height_entry.config(state="disabled")
                # DPI 输入框的状态也取决于 self.dpi_enabled.get()
                self.dpi_entry.config(state="normal" if self.dpi_enabled.get() else "disabled")
            else:  # 用户清空了DPI输入框
                self._enable_all_inputs_for_special_mode()
        else:  # source_changed is None (由模式切换触发，非直接输入)
            # 根据当前输入框的值决定哪个优先并禁用其他
            if width_str:  # 宽度有值，则宽度优先
                self.height_var.set("")
                self.height_entry.config(state="disabled")
                self.dpi_var.set("")
                self.dpi_entry.config(state="disabled")
                self.width_entry.config(state="normal")
            elif height_str:  # 高度有值，则高度优先
                self.width_var.set("")
                self.width_entry.config(state="disabled")
                self.dpi_var.set("")
                self.dpi_entry.config(state="disabled")
                self.height_entry.config(state="normal")
            elif dpi_str:  # DPI有值，则DPI优先
                self.width_var.set("")
                self.width_entry.config(state="disabled")
                self.height_var.set("")
                self.height_entry.config(state="disabled")
                self.dpi_entry.config(state="normal" if self.dpi_enabled.get() else "disabled")
            else:  # 所有相关输入框都为空
                self._enable_all_inputs_for_special_mode()

    def _enable_all_inputs_for_special_mode(self):
        """在特殊模式下，当所有相关输入都为空时，启用它们。"""
        self.width_entry.config(state="normal")
        self.height_entry.config(state="normal")
        # DPI 输入框的启用还需检查 self.dpi_enabled
        self.dpi_entry.config(state="normal" if self.dpi_enabled.get() else "disabled")


    # --- 包装 calc_utils 中的纯函数，保持旧接口 ---
    def convert_to_pixels(self, value, unit, dpi):
        return convert_to_pixels(value, unit, dpi)

    def convert_from_pixels(self, pixels, unit, dpi):
        return convert_from_pixels(pixels, unit, dpi)

    def calculate_optimal_dpi_for_image(self, original_width, original_height, target_width_inch, target_height_inch):
        return calculate_optimal_dpi_for_image(original_width, original_height, target_width_inch, target_height_inch)

    pass
